﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Net;
using System.ComponentModel;

namespace sabConn
{
    public class Queue : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {

            if (null != PropertyChanged)
            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        }


        #region Properties;
        private string _cache_size;
        public string cache_size { get { return _cache_size; } set { _cache_size = value; } }
        private bool _isPaused;
        public bool isPaused { get { return _isPaused; } set { _isPaused = value; } }
        private bool _restartReq;
        public bool restartReq { get { return _restartReq; } set { _restartReq = value; } }
        private string _uptime;
        public string uptime { get { return _uptime; } set { _uptime = value; } }
        private double _diskspacetotal2;
        public double diskspacetotal2 { get { return _diskspacetotal2; } set { _diskspacetotal2 = value; } }
        private double _diskspacetotal1;
        public double diskspacetotal1 { get { return _diskspacetotal1; } set { _diskspacetotal1 = value; } }
        private double _diskspace2;
        public double diskspace2 { get { return _diskspace2; } set { _diskspace2 = value; } }
        private double _diskspace1;
        public double diskspace1 { get { return _diskspace1; } set { _diskspace1 = value; } }
        private string _status;
        public string status { get { return _status; } set { _status = value; } }
        private string _finishAction;
        public string finishAction { get { return _finishAction; } set { _finishAction = value; } }
        private double _mbleft;
        public double mbleft { get { return _mbleft; } set { _mbleft = value; } }
        private TimeSpan _timeLeft;
        public TimeSpan timeLeft { get { return _timeLeft; } set { _timeLeft = value; } }
        private double _mb;
        public double mb { get { return _mb; } set { _mb = value; } }
        private string _eta;
        public string eta { get { return _eta; } set { _eta = value; } }
        private double _transferRate;
        public double transferRate { get { return _transferRate; } set { _transferRate = value; } }
        private double _speedLimit;
        public double speedLimit { get { return _speedLimit; } set { _speedLimit = value; } }
        private string _version;
        public string version { get { return _version; } set { _version = value; } }
        private List<Job> _jobs;
        public List<Job> jobs
        {
            get
            {
                return _jobs;
            }
            set
            {
                _jobs = value;
                OnPropertyChanged("jobs");
            }
        }
        private List<string> _categories;
        public List<string> categories { get { return _categories; } set { _categories = value; } }
        #endregion Properties;

        internal XmlDocument sabnzbxml;
        internal XmlDocument cats;
        internal ServerConnection m_parent;
        private XmlNodeList _xmljobs;

        public Queue(ServerConnection srvConn)
        {
            //fetch the Queue xml
            m_parent = srvConn;
            sabnzbxml = new XmlDocument();
            _jobs = new List<Job>();
            _categories = new List<string>();
            cats = new XmlDocument();
            loadCategories();
        }

        public void update()
        {
            updateXml();
            updateQueue();
            updateJobs();
        }

        private void updateXml()
        {
            string cmdstring = m_parent.serverstring + "/sabnzbd/api?mode=queue&start=START&limit=LIMIT&output=xml&apikey=" + m_parent.apikey;
            sabnzbxml.Load(cmdstring);
        }

        private void updateQueue()
        {
            //Refreshes all Queue properties.
            string ti_timeleft = "";
            string ti_speedLimit = "";
            string ti_diskspace1 = "";
            string ti_diskspace2 = "";
            string ti_diskspacetotal1 = "";
            string ti_diskspacetotal2 = "";
            string ti_mbleft = "";
            string ti_mb = "";
            string ti_isPaused = "";
            string ti_restartReq = "";
            string ti_transferRate = "";

            XmlNodeList elements = sabnzbxml.SelectNodes("queue");

            foreach (XmlNode element in elements)
            {
                _status = element.SelectSingleNode("status").InnerText;
                ti_timeleft = element.SelectSingleNode("timeleft").InnerText;
                ti_mbleft = element.SelectSingleNode("mbleft").InnerText;
                _version = element.SelectSingleNode("version").InnerText;
                ti_speedLimit = element.SelectSingleNode("speedlimit").InnerText;
                ti_diskspace2 = element.SelectSingleNode("diskspace2").InnerText;
                ti_diskspace1 = element.SelectSingleNode("diskspace1").InnerText;
                ti_diskspacetotal2 = element.SelectSingleNode("diskspacetotal2").InnerText;
                ti_diskspacetotal1 = element.SelectSingleNode("diskspacetotal1").InnerText;
                _uptime = element.SelectSingleNode("uptime").InnerText;
                _eta = element.SelectSingleNode("eta").InnerText;
                ti_mb = element.SelectSingleNode("mb").InnerText;
                _cache_size = element.SelectSingleNode("cache_size").InnerText;
                _finishAction = element.SelectSingleNode("finishaction").InnerText;
                ti_isPaused = element.SelectSingleNode("paused").InnerText;
                ti_restartReq = element.SelectSingleNode("restart_req").InnerText;
                ti_transferRate = element.SelectSingleNode("kbpersec").InnerText;
            }
            //Parse information
            try { _mbleft = Double.Parse(ti_mbleft); } catch { _mbleft = 0; }
            try { _transferRate = double.Parse(ti_transferRate); }
            catch { _transferRate = 0; }
            try { _diskspace1 = double.Parse(ti_diskspace1); }
            catch { _diskspace1 = 0; }
            try { _diskspace2 = double.Parse(ti_diskspace2); }
            catch { _diskspace2 = 0; }
            try { _diskspacetotal1 = double.Parse(ti_diskspacetotal1); }
            catch { _diskspacetotal1 = 0; }
            try { _diskspacetotal2 = double.Parse(ti_diskspacetotal2); }
            catch { _diskspacetotal2 = 0; }
            try { _isPaused = bool.Parse(ti_isPaused); }
            catch { _isPaused = false; }
            try { _restartReq = bool.Parse(ti_restartReq); }
            catch { _restartReq = false; }
            try { _mb = double.Parse(ti_mb); }
            catch { _mb = 0; }
            try { _speedLimit = double.Parse(ti_speedLimit); }
            catch { _speedLimit = 0; }
            try
            {
                string[] arr = ti_timeleft.Split(Char.Parse(":"));
                int hours, minutes, seconds;
                hours = int.Parse(arr[0]);
                minutes = int.Parse(arr[1]);
                seconds = int.Parse(arr[2]);
                _timeLeft = new TimeSpan(hours, minutes, seconds);
            }
            catch { _timeLeft = new TimeSpan(); }
        }

        private void updateJobs()
        {
            _xmljobs = sabnzbxml.SelectNodes("queue/slots/slot");
  
            foreach (XmlNode xmljob in _xmljobs)
            {
                string t_status = "", t_index = "", t_timeleft = "", t_age = "", t_msgid = "", t_mb = "";
                string t_mbleft = "", t_filename = "", t_priority = "", t_category = "", t_percentage = "";
                string t_nzo_id = "", t_eta = "";

                int i_index = 0;
                double i_percentage = 0, i_mb = 0, i_mbleft = 0;
                TimeSpan i_timeLeft;
                Job.Priority i_priority = Job.Priority.Normal;
                Job t_job;

                t_status = xmljob.SelectSingleNode("status").InnerText;
                t_index = xmljob.SelectSingleNode("index").InnerText;
                t_timeleft = xmljob.SelectSingleNode("timeleft").InnerText;
                t_age = xmljob.SelectSingleNode("avg_age").InnerText;
                t_msgid = xmljob.SelectSingleNode("msgid").InnerText;
                t_mb = xmljob.SelectSingleNode("mb").InnerText;
                t_mbleft = xmljob.SelectSingleNode("mbleft").InnerText;
                t_filename = xmljob.SelectSingleNode("filename").InnerText;
                t_priority = xmljob.SelectSingleNode("priority").InnerText;
                t_category = xmljob.SelectSingleNode("cat").InnerText;
                t_percentage = xmljob.SelectSingleNode("percentage").InnerText;
                t_nzo_id = xmljob.SelectSingleNode("nzo_id").InnerText;
                t_eta = xmljob.SelectSingleNode("eta").InnerText;

                if (t_index != string.Empty) { i_index = int.Parse(t_index); }
                if(t_mb != string.Empty) { i_mb = double.Parse(t_mb); }
                if(t_mbleft != string.Empty) { i_mbleft = double.Parse(t_mbleft); }
                if (t_percentage != string.Empty) { i_percentage = double.Parse(t_percentage); }
                try
                {
                    string[] arr = t_timeleft.Split(Char.Parse(":"));
                    int hours, minutes, seconds;
                    hours = int.Parse(arr[0]);
                    minutes = int.Parse(arr[1]);
                    seconds = int.Parse(arr[2]);
                    i_timeLeft = new TimeSpan(hours, minutes, seconds);
                }
                catch { i_timeLeft = new TimeSpan(); }

                if(t_priority != string.Empty)
                {
                    switch (t_priority)
                    {
                        case "Default":
                            i_priority = Job.Priority.Default;
                            break;
                        case "Low":
                            i_priority = Job.Priority.Low;
                            break;
                        case "Normal":
                            i_priority = Job.Priority.Normal;
                            break;
                        case "High":
                            i_priority = Job.Priority.High;
                            break;
                        default:
                            i_priority = Job.Priority.Normal;
                            break;
                    }
                }

                t_job = _jobs.FirstOrDefault(c => c.nzo_id == t_nzo_id);
                if (t_job == null) //Job not in list, insert
                {
                    t_job = new Job(t_status, i_index, t_eta, i_timeLeft, t_age, i_mbleft, i_mb, t_filename, i_priority, t_category, i_percentage, t_nzo_id, this);
                    jobs.Add(t_job);
                }
                else //update object in current list
                {
                    jobs[t_job.Index].status = t_status;
                    jobs[t_job.Index].priority = i_priority;
                    jobs[t_job.Index].category = t_category;
                    jobs[t_job.Index].percentage = i_percentage;
                    jobs[t_job.Index].timeleft = i_timeLeft;
                    jobs[t_job.Index].mbleft = i_mbleft;
                    jobs[t_job.Index].age = t_age;
                    jobs[t_job.Index].Filename = t_filename;
                    jobs[t_job.Index].Index = i_index;
                    jobs[t_job.Index].mb = i_mb;
                    jobs[t_job.Index].nzo_id = t_nzo_id;
                }
                OnPropertyChanged("jobs");
                OnPropertyChanged("bind_jobs");
            }
        }

        private void loadCategories()
        {
            string cmdstring = m_parent.serverstring + "/sabnzbd/api?mode=get_cats&output=xml&apikey=" + m_parent.apikey;
            try
            {
                cats.Load(cmdstring);
                XmlNodeList categories = cats.SelectNodes("categories/category");

                foreach (XmlNode x in categories)
                {
                    _categories.Add(x.InnerText);
                }
            }
            catch
            {
            }
        }

        public void delete(List<string> rmlist)
        {
            string nzblist="";
            List<Job> jlist = new List<Job>();

            foreach (string r in rmlist)
            {
                nzblist = nzblist + r + ",";
                jlist.Add(getJobByNzo(r));
            }
            nzblist = nzblist.TrimEnd(char.Parse(","));
            string cmdstring = m_parent.serverstring + "/sabnzbd/api?mode=queue&name=delete&value=" + nzblist + "&apikey=" + m_parent.apikey;
            m_parent.requestpage(cmdstring);
            foreach (Job j in jlist)
            {
                foreach (Job jj in jobs)
                {
                    if (j == jj)
                    {
                        jobs.Remove(j);
                        break;
                    }
                }
            }
        }

        public void deleteAll()
        {
            string cmdstring = m_parent.serverstring + "/sabnzbd/api?mode=queue&name=delete&value=all" + "&apikey=" + m_parent.apikey;
            m_parent.requestpage(cmdstring);
        }

        public void pause()
        {
            string cmdstring;
            cmdstring = m_parent.serverstring + "/sabnzbd/api?mode=pause&apikey=" + m_parent.apikey;
            m_parent.requestpage(cmdstring);
        }

        public void timedPause(int minutes)
        {
            string cmdstring;
            cmdstring = m_parent.serverstring + "api?mode=config&name=set_pause&value=" + minutes + "&apikey=" + m_parent.apikey;
            m_parent.requestpage(cmdstring);
        }

        public void resume()
        {
            string cmdstring;
            cmdstring = m_parent.serverstring + "/sabnzbd/api?mode=resume&apikey=" + m_parent.apikey;
            m_parent.requestpage(cmdstring);
        }

        public void shutdown()
        {
            string cmdstring;
            cmdstring = m_parent.serverstring + "/sabnzbd/api?mode=shutdown&apikey=" + m_parent.apikey;
            m_parent.requestpage(cmdstring);
        }

        public void restart()
        {
            string cmdstring;
            cmdstring = m_parent.serverstring + "/sabnzbd/api?mode=restart&apikey=" + m_parent.apikey;
            m_parent.requestpage(cmdstring);
        }

        public void addNzbUrl(string url, string nzbName, string category="")
        {
            string cmdstring;
            if (category == String.Empty)
            {
                cmdstring = m_parent.serverstring + "/sabnzbd/api?mode=addurl&name=" + url + "&nzbname=" + nzbName + "&apikey=" + m_parent.apikey;
                m_parent.requestpage(cmdstring);
            }
        }

        public void setSpeedLimit(double speed)
        {
            string cmdstring;
            if (speed > 0)
            {
                int ts = (int)Math.Round(speed, 0);
                if (ts > 0)
                {
                    cmdstring = m_parent.serverstring + "/sabnzbd/api?mode=config&name=speedlimit&value" + ts.ToString() + "&apikey=" + m_parent.apikey;
                    m_parent.requestpage(cmdstring);
                }
            }
        }

        public string AddNzbFile(string FileName)
        {
            string str4;
            string url = m_parent.serverstring + "/sabnzbd/api?mode=addfile&name=" + FileName + "&ma_username=" + m_parent.username + "&ma_password=" + m_parent.password + "&apikey=" + m_parent.apikey;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.AllowWriteStreamBuffering = true;
            string str = Guid.NewGuid().ToString();
            request.ContentType = string.Format("multipart/form-data; boundary={0}", str);
            request.Method = "POST";
            string str2 = string.Format("--{0}", str);
            string str3 = str2 + "--";
            StringBuilder builder = new StringBuilder();
            builder.AppendLine(str2);
            builder.AppendLine(string.Format("Content-Disposition: form-data; name=\"name\"; filename=\"{0}\"", FileName));
            builder.AppendLine("Content-Type: text/xml");
            builder.AppendLine();
            builder.AppendLine(Encoding.UTF8.GetString(File.ReadAllBytes(FileName)));
            builder.AppendLine(str3);
            byte[] bytes = Encoding.UTF8.GetBytes(builder.ToString());
            request.ContentLength = bytes.Length;
            using (Stream stream = request.GetRequestStream())
            {
                stream.Write(bytes, 0, bytes.Length);
                stream.Flush();
                stream.Close();
                using (WebResponse response = request.GetResponse())
                {
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        str4 = reader.ReadToEnd();
                    }
                }
            }
            return str4;
        }

        private Job getJobByNzo(string nzo)
        {
            foreach (Job j in jobs)
            {
                if (j.nzo_id == nzo)
                {
                    return j;
                }
            }
            return new Job("", 0, "", new TimeSpan(), "", 0, 0, "", Job.Priority.Default, "", 0, "", this);
        }

    }


}
